package com.hzw.saas.service.product.service;

import javax.annotation.Resource;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.product.IUserBalanceService;
import com.hzw.saas.api.product.dto.UserBalanceDto;
import com.hzw.saas.api.user.IUserAccountService;
import com.hzw.saas.common.config.annotation.RedisProcessLock;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.util.IdWorker;
import com.hzw.saas.common.util.enums.AssertEnum;
import com.hzw.saas.service.product.mapper.UserBalanceMapper;
import com.hzw.saas.service.product.model.UserBalance;
import cn.hutool.core.util.NumberUtil;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import ma.glasnost.orika.MapperFacade;

/**
 * <p>
 * 用户余额表 服务实现类
 * </p>
 *
 * @author zzl
 * @since 2021-01-21
 */
@Service("userBalanceServiceImpl")
@RequiredArgsConstructor
public class UserBalanceServiceImpl extends ServiceImpl<UserBalanceMapper, UserBalance> implements IUserBalanceService, IService<UserBalance> {

    private final MapperFacade mapperFacade;
    private final IdWorker idWorker;
    @Resource(name = "userAccountServiceImpl")
    private IUserAccountService userAccountService;

    @Override
    public UserBalanceDto get(String userId) {
        if (StringUtils.isBlank(userId)) {
            throw DefaultSaasException.build("用户ID为空").status(HttpStatus.BAD_REQUEST);
        }
        checkUser(userId);
        UserBalance userBalance = this.lambdaQuery().eq(UserBalance::getUserId, userId).one();
        if (userBalance == null) {
            userBalance = new UserBalance();
            userBalance.setBalanceId(String.valueOf(idWorker.nextId()));
            userBalance.setUserId(userId);
            userBalance.setBalance(0D);
            this.save(userBalance);
        }
        return mapperFacade.map(userBalance, UserBalanceDto.class);
    }

    @Override
    public boolean update(UserBalanceDto userBalanceDto) {
        // 检查balanceId和userId是否同时为空
        if (StringUtils.isBlank(userBalanceDto.getBalanceId()) && StringUtils.isBlank(userBalanceDto.getUserId())) {
            throw DefaultSaasException.build("balanceId和userId同时为空").status(HttpStatus.BAD_REQUEST);
        }
        // 当balanceId为空时，根据userId获取balanceId
        if (StringUtils.isBlank(userBalanceDto.getBalanceId())) {
            UserBalanceDto temp = this.get(userBalanceDto.getUserId());
            userBalanceDto.setBalanceId(temp.getBalanceId());
        }
        UserBalance userBalance = mapperFacade.map(userBalanceDto, UserBalance.class);
        return this.updateById(userBalance);
    }

    @Override
    @RedisProcessLock(lockName = "ChangeBalance", key = "#userId")
    public double addBalance(String userId, double value) {
        checkUser(userId);
        return changeBalance(userId, value, true);
    }

    @Override
    @RedisProcessLock(lockName = "ChangeBalance", key = "#userId")
    public double subBalance(String userId, double value) {
        checkUser(userId);
        return changeBalance(userId, value, false);
    }

    private void checkUser(String userId) {
        userAccountService.assertExist(userId, AssertEnum.FOUND);
    }

    private double changeBalance(String userId, double value, boolean add) {
        if (NumberUtil.compare(value, 0) < 0) {
            throw DefaultSaasException.build("充值或消费金额需大于0").status(HttpStatus.BAD_REQUEST);
        }
        UserBalanceDto now = get(userId);
        double balance = now.getBalance() == null ? 0 : now.getBalance();
        double result = 0;
        if (add) {
            // 充值
            result = NumberUtil.add(balance, value);
        } else {
            // 消费
            if (NumberUtil.compare(balance, value) < 0) {
                throw DefaultSaasException.build("余额不足").status(HttpStatus.BAD_REQUEST);
            }
            result = NumberUtil.sub(balance, value);
        }
        boolean update = this.lambdaUpdate().set(UserBalance::getBalance, result).eq(UserBalance::getBalanceId,
            now.getBalanceId()).eq(UserBalance::getBalance, now.getBalance()).update();
        if (!update) {
            throw DefaultSaasException.build("余额更新失败!");
        }
        return result;
    }
}
